/**
 * Copyright 2012 Alfredo "Rainbowbreeze" Morresi
 * 
 * This file is part of Apps4Italy PM10 project.
 * 
 * PM10 is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * PM10 is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with Foobar. If not, see http://www.gnu.org/licenses/.
 */
package it.rainbowbreeze.eureka.common;

import it.rainbowbreeze.eureka.data.AppPreferencesDao;
import it.rainbowbreeze.eureka.data.QualityDataPointDao;
import it.rainbowbreeze.eureka.data.RestfulClient;
import it.rainbowbreeze.eureka.data.WebserviceManager;
import it.rainbowbreeze.eureka.logic.FiltersManager;
import it.rainbowbreeze.eureka.logic.JobExecutor;
import it.rainbowbreeze.eureka.logic.JobFactory;
import it.rainbowbreeze.eureka.logic.JsonConverter;
import it.rainbowbreeze.eureka.logic.LogicManager;
import it.rainbowbreeze.eureka.logic.QualityDataPointManager;
import it.rainbowbreeze.eureka.ui.ActivityHelper;
import it.rainbowbreeze.libs.common.RainbowServiceLocator;
import it.rainbowbreeze.libs.logic.RainbowCrashReporter;
import android.content.Context;

import static it.rainbowbreeze.libs.common.RainbowContractHelper.checkNotNull;

/**
 * Main application classes lazy-loading singleton
 * 
 * @author Alfredo "Rainbowbreeze" Morresi
 */
public class AppEnv {
    // ------------------------------------------ Private Fields
    private static final String LOG_HASH = "AppEnv";
    private static final Object mSyncObject = new Object();

    
    // -------------------------------------------- Constructors
    private AppEnv(Context context) {
        //use default objects factory
        this(context, getDefaultObjectsFactory());
    }
    
    private AppEnv(Context context, ObjectsFactory objectsFactory) {
        //use a custom object factory
        checkNotNull(objectsFactory, "ObjectsFactory");
        setupVolatileData(context, objectsFactory);
    }

    
    // --------------------------------------- Public Properties
    /** keys for application preferences */
    public final static String APP_PREFERENCES_KEY = "PolygenPrefs";

    /** lazy loading singleton */
    public static AppEnv i(Context context) {
        synchronized (mSyncObject) {
            if (null == mInstance)
                mInstance = new AppEnv(context);
        }
        return mInstance;
    }
    /** lazy loading singleton, reload the environment each time (for testing purposes) */
    public static AppEnv i(Context context, ObjectsFactory objectsFactory) {
        synchronized (mSyncObject) {
            if (null == mInstance) {
                mInstance = new AppEnv(context, objectsFactory);
            } else {
                //force the rebuild of all the environment
                mInstance.setupVolatileData(context, objectsFactory);
            }
        }
        return mInstance;
    }
    private static AppEnv mInstance;

    
    /** Application version displayed to the user (about activity etc) */
    public final static String APP_DISPLAY_VERSION = "1.2";
    
    /** Application name used during the ping of update site */
    public final static String APP_INTERNAL_NAME = "Eureka!";

    /** Application version for internal use (update, crash report etc) */
    public final static String APP_INTERNAL_VERSION = "01.02.03";

    /** Application version for internal use (update, crash report etc) */
    public final static String EMAIL_FOR_ERROR_LOG = "alfredo.morresi@gmail.com";
    
    /** Tag to use in the log */
    public final static String LOG_TAG = "Eureka";
    
    /** Not found item in content provider */
    public static final long NOT_FOUND = -1;

    /** Default objects factory, testing purposes */
    protected static final ObjectsFactory defaultObjectsFactory = new ObjectsFactory();
    public static final ObjectsFactory getDefaultObjectsFactory()
    { return defaultObjectsFactory; }

    /** Base address for polygen webservice */
    public static final String BASE_SERVER_URL = "http://eureka.fullo.net/";
    
    /** Number of quality level, where 0 is worse quality, MAX is best quality */
    public static final int MAX_LEVEL = 5;
    
    
    // ------------------------------------------ Public Methods

    public LogFacility getLogFacility() {
        return checkNotNull(RainbowServiceLocator.get(
                LogFacility.class), LogFacility.class.getSimpleName());
    }
    
    public AppPreferencesDao getAppPreferencesDao() {
        return checkNotNull(RainbowServiceLocator.get(
                AppPreferencesDao.class), AppPreferencesDao.class.getSimpleName());
    }
    
    public QualityDataPointDao getQualityDataPointDao() {
        return checkNotNull(RainbowServiceLocator.get(
                QualityDataPointDao.class), QualityDataPointDao.class.getSimpleName());
    }
    
    public LocationListenerQueue getPositionListenerLocator() {
        return checkNotNull(RainbowServiceLocator.get(
                LocationListenerQueue.class), LocationListenerQueue.class.getSimpleName());
    }
    
    public JsonConverter getJsonConverter() {
        return checkNotNull(RainbowServiceLocator.get(
                JsonConverter.class), JsonConverter.class.getSimpleName());
    }
    
    public LogicManager getLogicManager() {
        return checkNotNull(RainbowServiceLocator.get(
                LogicManager.class), LogicManager.class.getSimpleName());
    }
    
    public FiltersManager getFiltersManager() {
        return checkNotNull(RainbowServiceLocator.get(
                FiltersManager.class), FiltersManager.class.getSimpleName());
    }
    
    public WebserviceManager getWebserviceManager() {
        return checkNotNull(RainbowServiceLocator.get(
                WebserviceManager.class), WebserviceManager.class.getSimpleName());
    }
    
    public QualityDataPointManager getQualityDataPointManager() {
        return checkNotNull(RainbowServiceLocator.get(
                QualityDataPointManager.class), QualityDataPointManager.class.getSimpleName());
    }
    
    public JobExecutor getJobExecutor() {
        return checkNotNull(RainbowServiceLocator.get(
                JobExecutor.class), JobExecutor.class.getSimpleName());
    }
    
    public JobFactory getJobFactory() {
        return checkNotNull(RainbowServiceLocator.get(
                JobFactory.class), JobFactory.class.getSimpleName());
    }
    
    public ActivityHelper getActivityHelper() {
        return checkNotNull(RainbowServiceLocator.get(
                ActivityHelper.class), ActivityHelper.class.getSimpleName());
	}
    
    // ----------------------------------------- Private Methods
    /**
     * Setup the volatile data of application.
     * This is needed because sometime the system release memory
     * without completely close the application, so all static fields
     * will become null :(
     * 
     */
    private void setupVolatileData(Context context, ObjectsFactory mObjectsFactory) {
        //set the log tag
        LogFacility logFacility = mObjectsFactory.createLogFacility(LOG_TAG);
        logFacility.i(LOG_HASH, "Initializing environment");

        //empty service locator
        RainbowServiceLocator.clear();
        //put log facility
        RainbowServiceLocator.put(logFacility);

        //initialize (and automatically register) crash reporter
        RainbowCrashReporter crashReport = mObjectsFactory.createCrashReporter(context);
        RainbowServiceLocator.put(crashReport);
        //create services and helper respecting IoC dependencies
        ActivityHelper activityHelper = mObjectsFactory.createActivityHelper(context, logFacility);
        RainbowServiceLocator.put(activityHelper);
        AppPreferencesDao appPreferencesDao = mObjectsFactory.createAppPreferencesDao(context, APP_PREFERENCES_KEY);
        RainbowServiceLocator.put(appPreferencesDao);
        QualityDataPointDao qualityDataPointDao = mObjectsFactory.createQualityDataPointDao(logFacility);
        RainbowServiceLocator.put(qualityDataPointDao);
        JsonConverter jsonConverter = mObjectsFactory.createJsonConverter(logFacility);
        RainbowServiceLocator.put(jsonConverter);
        RestfulClient restfulClient = mObjectsFactory.createRestfulClient(logFacility);
        RainbowServiceLocator.put(restfulClient);
        WebserviceManager webserviceManager = mObjectsFactory.createWebserviceManager
                (logFacility, restfulClient, jsonConverter);
        RainbowServiceLocator.put(webserviceManager);
        JobExecutor jobExecutor = mObjectsFactory.createJobExecutor(logFacility);
        RainbowServiceLocator.put(jobExecutor);
        JobFactory jobFactory = mObjectsFactory.createJobFactory
                (logFacility, webserviceManager);
        RainbowServiceLocator.put(jobFactory);
        QualityDataPointManager qualityDataPointManager = mObjectsFactory.createQualityDataPointManager
                (logFacility, qualityDataPointDao, jobExecutor, jobFactory);
        RainbowServiceLocator.put(qualityDataPointManager);
        LogicManager logicManager = mObjectsFactory.createLogicManager(
                logFacility,
                appPreferencesDao,
                AppEnv.APP_INTERNAL_VERSION);
        RainbowServiceLocator.put(logicManager);
        FiltersManager filtersManager = mObjectsFactory.createFiltersManager(logFacility, appPreferencesDao);
        RainbowServiceLocator.put(filtersManager);
        LocationListenerQueue positionListenerLocator = mObjectsFactory.createPositionListenerLocator(logFacility);
        RainbowServiceLocator.put(positionListenerLocator);
    }
    
    
    // ----------------------------------------- Private Classes
    public static class ObjectsFactory {
        public LogFacility createLogFacility(String logTag)
        { return new LogFacility(logTag); }
        
        public RainbowCrashReporter createCrashReporter(Context context)
        { return new RainbowCrashReporter(context); }
        
        public ActivityHelper createActivityHelper(Context context, LogFacility logFacility)
        { return new ActivityHelper(logFacility, context); }
        
        public AppPreferencesDao createAppPreferencesDao(Context context, String appPreferencesKey)
        { return new AppPreferencesDao(context, appPreferencesKey); }
        
        public QualityDataPointDao createQualityDataPointDao(LogFacility logFacility)
        { return new QualityDataPointDao(logFacility); }

        public LocationListenerQueue createPositionListenerLocator(LogFacility logFacility)
        { return new LocationListenerQueue(logFacility); }
        
        public JsonConverter createJsonConverter(LogFacility logFacility)
        { return new JsonConverter(logFacility); }
        
        public RestfulClient createRestfulClient(LogFacility logFacility)
        { return new RestfulClient(logFacility); }
        
        public WebserviceManager createWebserviceManager(
                LogFacility logFacility,
                RestfulClient restfulClient,
                JsonConverter jsonConverter)
        { return new WebserviceManager(logFacility, restfulClient, jsonConverter); }
        
        public QualityDataPointManager createQualityDataPointManager(
                LogFacility logFacility,
                QualityDataPointDao qualityDataPointDao, JobExecutor jobExecutor, JobFactory jobFactory)
        { return new QualityDataPointManager(logFacility, qualityDataPointDao, jobExecutor, jobFactory); }
        
        public JobExecutor createJobExecutor(LogFacility logFacility)
        { return new JobExecutor(logFacility); }
        
        public JobFactory createJobFactory(
                LogFacility logFacility,
                WebserviceManager webserviceManager) {
            return new JobFactory(
                    logFacility,
                    webserviceManager);
        }
        
        public LogicManager createLogicManager(
                LogFacility logFacility,
                AppPreferencesDao appPreferencesDao,
                String currentAppVersion) {
            return new LogicManager(
                logFacility,
                appPreferencesDao,
                currentAppVersion);
        }
        
        public FiltersManager createFiltersManager(LogFacility logFacility, AppPreferencesDao appPreferencesDao)
        { return new FiltersManager(logFacility, appPreferencesDao); }
        
        
    }

}
